`include "defines.v"

module ID_Stage (
    input  wire [63: 0]    pc_i,
    input  wire [`INSTLEN] inst,

    input  wire         ForwardA,
    input  wire         ForwardB,
    input  wire         ForwardCSR,
    input  wire [`xlen] forward_rs1_data,
    input  wire [`xlen] forward_rs2_data, 
    input  wire [`xlen] forward_csr_data,

    output wire [63: 0] pc_o,

    input  wire [`xlen] rs1_data_i,
    input  wire [`xlen] rs2_data_i,
    input  wire [`xlen] csr_data_i,
    output wire [`xlen] regbusA,
    output wire [`xlen] regbusB,
    output wire         rs1_data_sign,

    //oprend generate    
    output reg  [`xlen] imm,

    output wire         rs1_ena,
    output wire [ 4: 0] rs1_raddr,
    output wire         rs2_ena,
    output wire [ 4: 0] rs2_raddr,

    output wire [ 4: 0] rd_addr,

    output wire         csr_ena,
    output wire [11: 0] csr_addr,
    //

    //ALU oprend&operation control
    output wire [ 1: 0] ALU_oprendA_src,
    output wire [ 1: 0] ALU_oprendB_src,
    output reg  [ 2: 0] ALU_op,
    output wire [ 2: 0] ALU_out_ext_type,
    //

    //ldr&sdr insts
    output wire [ 3: 0] MemWr,
    output wire         MemToReg,
    output wire         RegWr,
    output wire [ 2: 0] load_ext_type,
    //

    //branch&jump flags
    output wire         adder_src,
    output wire [ 2: 0] branch,
    output wire         jump,
    //

    output wire         is_system_inst
);
//regbus mux
assign regbusA = rs1_data;
assign regbusB = is_system_inst ? csr_data : rs2_data;
//

//forwarding
wire [`xlen] rs1_data;
wire [`xlen] rs2_data;
wire [`xlen] csr_data;
assign rs1_data = (ForwardA & rs1_ena) ? forward_rs1_data : rs1_data_i;
assign rs2_data = (ForwardB & rs2_ena) ? forward_rs2_data : rs2_data_i;
assign rs1_data_sign = rs1_data[31];
assign csr_data = (ForwardCSR & is_system_inst) ? forward_csr_data : csr_data_i;

//instruction decode

wire [ 4: 0] opcode;
wire [ 2: 0] funct3;
wire [ 6: 0] funct7;
wire [ 5: 0] shamt;
wire [`xlen] imm_I_shift;
wire [`xlen] imm_I_shift_w;
wire [`xlen] imm_I_nonshift;
wire [`xlen] imm_S;
wire [`xlen] imm_B;
wire [`xlen] imm_U;
wire [`xlen] imm_J;
reg  [ 2: 0] imm_extend_type;

assign opcode    = inst[ 6: 2];
assign rd_addr   = inst[11: 7];
assign funct3    = inst[14:12];
assign rs1_raddr = inst[19:15];
assign rs2_raddr = inst[24:20];
assign funct7    = inst[31:25];
assign shamt     = inst[25:20];
assign csr_addr  = inst[31:20];

//immediate generate
assign imm_I_shift = {58'b0,shamt};
assign imm_I_shift_w = {59'b0,shamt[4:0]};
assign imm_I_nonshift = { {52{inst[31]}} , inst[31:20] };
assign imm_B = { {51{inst[31]}} , inst[31] , inst[7] , inst[30:25] , inst[11:8] , 1'b0 };
assign imm_S = { {52{inst[31]}} , inst[31:25] , inst[11:7] };
assign imm_U = { {32{inst[31]}} , inst[31:12] , 12'b0 };
assign imm_J = { {44{inst[31]}} , inst[19:12] , inst[20] , inst[30:21] , 1'b0 };


always@(*) begin
    casez(opcode)
    5'b001?0 : begin
        imm_extend_type = (~funct3[1] & funct3[0] & ~opcode[1]) ? `imm_I_s_extend   : 
                          (~funct3[1] & funct3[0] & opcode[1])  ? `imm_I_s_w_extend :  `imm_I_ns_extend;
    end
    5'b00000 : begin                                //load insts
        imm_extend_type = `imm_I_ns_extend; 
    end
    5'b11001 : begin                                //jalr
        imm_extend_type = `imm_I_ns_extend; 
    end 
    `B_type : begin
        imm_extend_type = `imm_B_extend;  
    end   
    `S_type : begin
        imm_extend_type = `imm_S_extend; 
     end
     `J_type : begin
        imm_extend_type = `imm_J_extend; 
     end
     `U_type : begin
        imm_extend_type = `imm_U_extend; 
     end 
     default : imm_extend_type = `imm_noextend;
     endcase
end

always @(*) begin
    case (imm_extend_type)
        `imm_I_s_extend   : begin
            imm = imm_I_shift;
        end
        `imm_I_s_w_extend : begin
            imm = imm_I_shift_w;
        end
        `imm_I_ns_extend  : begin
            imm = imm_I_nonshift;
        end
        `imm_B_extend     : begin
            imm = imm_B;
        end
        `imm_S_extend     : begin
            imm = imm_S;
        end
        `imm_U_extend     : begin
            imm = imm_U;
        end
        `imm_J_extend     : begin
            imm = imm_J;
        end
        default: imm = 64'b0;
    endcase
end
//


//B&J type
assign pc_o = pc_i;
assign adder_src = opcode[4] & opcode[3] & ~opcode [2] & ~opcode[1] & opcode [0];
// B-type: 11000
assign branch = (opcode[4] & opcode[3] & ~opcode[2] & ~opcode[1] & ~opcode[0]) ? funct3 : `notbranch ;

// jal:11011;jalr:11001;
assign jump = opcode[4] & opcode[3] & ~opcode [2] & opcode [0];
//

//privileged inst
assign is_system_inst = opcode[4] & opcode[3] & opcode[2] & ~opcode[1] & ~opcode[0];
assign csr_ena = is_system_inst;
//

//ALU
//ALU_out_ext
//[0] : 64bit; [1] : cmp type;(1 unsigned  /  0 signed) [2] : is_cmp
assign ALU_out_ext_type[0] = opcode[2] & opcode[1] & ~opcode[0];
assign ALU_out_ext_type[1] = funct3[0];
assign ALU_out_ext_type[2] = ~opcode[4] & opcode[2] & ~opcode[1] & ~opcode[0] & ~funct3[2] & funct3[1];

//oprendA
//pc:00;                           regbusA:01;
//zext(bitsel[rs1]):10;            sext(bitsel[rs1]):11
assign ALU_oprendA_src[0] = ~((opcode[4] & opcode[3] & ~opcode[2] & opcode[0])                    //jal&jalr
                           |(~opcode[4] & ~opcode[3] & opcode[2] & ~opcode[1] & opcode[0])       //auipc
                           |(ALU_out_ext_type[0] & funct3[2] & ~funct3[1] & funct3[0] & ~inst[30]));          //srlw&srliw
assign ALU_oprendA_src[1] = (ALU_out_ext_type[0] & funct3[2] & ~funct3[1] & funct3[0]);                      //srlw&srliw&sraw&sraiw
assign rs1_ena = (|ALU_oprendA_src & ~opcode[0]) | (opcode[4] & opcode[3] & ~opcode[2] & ~opcode[1] & opcode[0]) ;

//oprendB
//imm:00;                      "4":01;
//reg:10;                       zext(bitsel(rs2)):11;
assign ALU_oprendB_src[0] = ((ALU_out_ext_type[0] & opcode[3] & |funct3) | opcode[4] & opcode[3] & ~opcode[2] & opcode[0]) | ~opcode[4] & opcode[3] & opcode[2] & ~opcode[1] & ~opcode[0] & ~funct3[1] & funct3[0];
assign ALU_oprendB_src[1] = ((ALU_out_ext_type[0] & opcode[3]) | ~opcode[4] & opcode[3] & opcode[2] & ~opcode[0] | opcode[4] & opcode[3] & (opcode[2] | ~opcode[2] & ~opcode[1] & ~opcode[0]));
assign rs2_ena = ALU_oprendB_src[1] | ( ~opcode[4] & opcode[3] & ~opcode[2] & ~opcode[1] & ~opcode[0] );

//ALU_op
always@(*)begin
    casez({opcode,funct3})
    {5'b110?1,3'b???} : begin               //jal&jalr
        ALU_op = `add;
    end
    {5'b0?101,3'b???} : begin               //U_type
        ALU_op = `add;
    end
    {5'b0?000,3'b???} : begin               //load&store
        ALU_op = `add;
    end
    {5'b001?0,3'b000} : begin               //addi(w)
        ALU_op = `add;
    end
    {`R_type,3'b000} : begin                //R_add(w) sub(w)
        if(~inst[30]) ALU_op = `add;
        else          ALU_op = `sub;
    end
    {`B_type,3'b???} : begin                //B_type
        ALU_op = `sub;
    end
    {5'b0?100,3'b01?} : begin           //slti sltiu slt sltu
        ALU_op = `sub;
    end
    {5'b0?100,3'b111} : begin
        ALU_op = `and;
    end
    {5'b0?100,3'b110} : begin
        ALU_op = `or;
    end
    {5'b0?100,3'b100} : begin
        ALU_op = `xor;
    end
    {5'b0?1?0,3'b001} : begin
        ALU_op = `sll;
    end
    {5'b0?1?0,3'b101} : begin
        if (inst[30]) ALU_op = `sra;
        else          ALU_op = `srl;
    end
    {`privileged,3'b?10} : begin
        ALU_op = `or;
    end
    default : ALU_op = `add; 
    endcase
end
//

//MemWr[2]:S_type;            MemWr[1:0] 00:byte;  01:half word;  10:word;  11:d;
assign MemWr[2] = ~opcode[4] & opcode[3] & ~opcode[2] & ~opcode[1] & ~opcode[0];
assign MemWr[1:0] = funct3[1:0] ;

assign MemToReg = ~(|opcode);

assign RegWr = ~opcode[3] | opcode[2] | opcode[1] | opcode[0];

assign load_ext_type = (MemToReg & RegWr) ? funct3 : 3'b0 ;

endmodule